//@ts-check
//1. Promise是一个类，在执行这个类的时候需要传递一个执行器进去，这个执行器会立即执行
//2. Promise 三种状态，成功fulfilled，失败rejected，等待pending
//3. resolve, reject 函数用来更改状态 resolve => fulfilled reject => rejected
//4. then 方法  判断状态为fulfilled，调用成功回调函数successCallback 判断状态为rejected调用失败回调 failCallback  
//5. then 成功回调有一个参数，表示成功后的值 value
 //  then 失败回调有一个参数，表示失败的原因 reson
 //6. Promise 中加入异步setTimeOut，then会比setTimeOut先调用
 // 此时调用.then 方法，Promise状态为pending
 // 将successCallback 和 errorCallback  存储起来
 // 异步调用执行完成后，调用resolve或reject时，调用之前存储的成功回调和失败回调
//7. then方法多次调用
// 每一个then方法中传递的回调函数都要执行
//每一个then方法中传递的回调函数都要被存储
//异步调用执行完成后，调用resolve reject 时。一次调用之前存储的回调函数
// 8. then方法时可以被链式调用的，后面的then方法拿到上一个then方法回调函数的返回值
// then方法链式调用， 返回Promise对象
// 拿到上一个then方法回调函数的返回值
// 9. 如果then方法返回的是一个Promise对象，判断状态，决定调用resolve，reject
// 10. then方法链式调用识别Promise对象返回
// 报出错误Chaining cycle detected for promise
//11. 捕获错误
//执行期当中的代码执行过程中发生错误，更改为错误状态 reject(e)
// then 方法中的回调函数在执行过程中发生错误，下一个then方法中报出错误
// 12. 将then方法的参数变为可选参数
// promise.then().then().then(value => console.log(value))
//13. Promise.all
//14. Promise.resolve
//返回一个promise对象，参数为普通值或promise对象
// 15. finally
//无论时当前promise对象最终时成功还是失败，都会被调用
//后面可以用then方法，得到最终值
//定义在原型对象上
//finally方法的回调函数中可以return一个promise对象，此时应该等待这个promise对象执行完成后，再执行下一个then
// 16. catch
//定义在原型对象上

import { unwatchFile } from "fs"

//catch(failCallback){return this.then(undefined,failCallback)}
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
export default class MyPromise {
    constructor(executor){
        //执行期错误，变为错误状态
        try {
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
    }

    status = PENDING //初始化状态常量为等待
    value = undefined //成功的值
    reason = undefined //失败的原因
    successCallback = [] //定义为数组，then链式调用，多次调用Promise
    failCallback = []
    resolve = (value) => {
        //如果状态不是等待，阻止程序向下执行
        if(this.status != PENDING) return
        //修改状态
        this.status = FULFILLED 
        //保存成功的值
        this.value = value
        //判断成功回调是否存在，存在，将[]中的头部取出callback 调用
        while(this.successCallback.length) this.successCallback.shift()()
    }

    reject = (reason) => {
        if(this.status!=PENDING) return
        this.status = REJECTED
        this.reason = reason
        while(this.failCallback.length) this.failCallback.shift()()
    }

    then = (successCallback,failCallback) => {
        //then方法的链式调用 ，返回Promise对象
        let promise2 =  new MyPromise((resolve,reject) => {
            //判断状态
            if(this.status == FULFILLED){
                setTimeout(()=>{
                    try{
                       const x = successCallback(this.value) //上一次的结果
                       // 判断x 的值是普通值还是promise对象
                       // 如果是普通值，直接resolve
                       // 如果是promise对象，查看promise对象返回结果
                       // 再根据promise对象返回结果，决定调用resolve，还是reject
                       // 使用异步代码，获取到promise2
                       resolvePromise(promise2, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            }else if(this.status == REJECTED){
                setTimeout(() => {
                    try{
                        const x = failCallback(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            }else if(this.status == PENDING){
                // 在Promise类中加入异步
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try{
                            const x = successCallback(this.value)
                            console.log(x)
                            resolvePromise(promise2,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try{
                            const x = failCallback(this.value)
                            resolvePromise(promise2,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
            }
        })

        return promise2
    }

    static resolve (value){
        //判断参数是否为一个promise
        //是，直接返回promise
        //不是，把值包起来，返回一个promise对象
        if(value instanceof MyPromise) return value
        return new MyPromise((resolve) => resolve(value))
    }

    finally = (callback) => {
        //无论当前promise对象最终是成功还是失败，都会被调用
        //可以用then方法得到最终值
        //finally 方法的回调函数中可以return一个promise对象，此时应该等待这个promise对象执行完成之后，再执行下一个then
        return this.then(value => {
            return MyPromise.resolve(callback()).then(() => value)
        },reason => {
            return MyPromise.resolve(callback()).then(() => {throw reason})
        })
    }

    catch(failCallback){
        return this.then(undefined,failCallback)
    }

    static all(array){
        const result = []
        let index = 0
        return new MyPromise((resolve,reject) =>{
            const addData = (key, value) => {
                result[key] =value
                index ++
                if(index == array.length){
                    resolve(result)
                }
            }

            for(let i=0; i<array.length; i++){
                let item = array[i]
                if(item instanceof MyPromise){
                    item.then(value =>addData(i,value),reason => reject(reason))
                }
            }
            resolve()
        })
    }
    
    
}

const resolvePromise = (promise2, x, resolve, reject) => {
    if(promise2 === x){ //自己返回自己
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if(x instanceof MyPromise){
        //promise对象
        // x.then(value => resolve(value), reason => reject(reason))
        x.then(resolve,reject)
    }else{
        //普通值
        resolve(x)
    }
}



